package StarterJWT

import (
	"errors"
	"net/http"
	"strings"
	"time"

	"gitee.com/go-spring2/spring-core/gs"
	"gitee.com/go-spring2/spring-core/web"
	"gitee.com/go-spring2/spring-plus/gsp"
	"github.com/dgrijalva/jwt-go"
	uuid "github.com/satori/go.uuid"
)

var (
	TokenExpired     = errors.New("Token is expired")
	TokenNotValidYet = errors.New("Token not active yet")
	TokenMalformed   = errors.New("That's not even a token")
	TokenInvalid     = errors.New("Couldn't handle this token:")
)

func init() {
	gs.Object(new(JWTService)).Init(func(c *JWTService) {
		c.initJWT()
	})
}

type WebJWTFilter struct {
	s          []string
	JWTService *JWTService `autowire:""`
}

func GetUser(webCtx web.Context) (*JWTClaims, error) {
	user := webCtx.Get("user")
	if user != nil {
		return user.(*JWTClaims), nil
	}
	return nil, errors.New("未授权")
}
func NewWebJWTFilter(s ...string) *WebJWTFilter {
	return &WebJWTFilter{s: s}
}

func (f *WebJWTFilter) URLPatterns() []string {
	return f.s
}

func (f *WebJWTFilter) Invoke(ctx web.Context, chain web.FilterChain) {
	tokenString := ctx.Header("Authorization")
	if len(tokenString) < 8 || !strings.HasPrefix(tokenString, "Bearer") {
		//return "", errors.New("缺少Authorization Bearer")
		panic(gsp.NewRestError(http.StatusUnauthorized, "Unauthorized"))
		return
	}

	//claims, err := f.JWTService.ParseToken(tokenString)
	//util.NewPanicCond(func() interface{} { return gsp.New400 }).When(err != nil)

	//err = ctx.Set("user", claims)
	//util.NewPanicCond(func() interface{} { return gsp.New400 }).When(err != nil)

	// 处理请求
	chain.Next(ctx, web.Iterative)
}

type JWTClaims struct {
	UUID     uuid.UUID
	ID       uint64
	Username string
	NickName string
	RoleId   string
}

type baseClaims struct {
	UUID     uuid.UUID `json:"uuid,omitempty"`
	ID       uint64    `json:"id,omitempty"`
	Username string    `json:"name,omitempty"`
	RoleId   string    `json:"roleId,omitempty"`
	jwt.StandardClaims
}

type RefreshTokenResp struct {
	Token        string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	NowTime      int64  `json:"now_time"`
	ExpiresTime  int64  `json:"expires_time"`
}

type JWTService struct {
	Issuer             string `value:"${jwt.issuer:=hmstudio}"`             // 签名的发行者
	ExpiresTime        int64  `value:"${jwt.expires-time:=600}"`            // token过期时间
	SigningKey         string `value:"${jwt.key:=hmstudio}"`                // token签名
	RefreshExpiresTime int64  `value:"${jwt.refresh-expires-time:=604800}"` // refreshToken过期时间
	RefreshSigningKey  string `value:"${jwt.refresh-key:=hmstudio}"`        // refreshToken签名
}

func (s *JWTService) initJWT() {
	// ctx.LogInfo("::JWTService#initJWT before " + s.Issuer + " " + s.SigningKey)
	if s.Issuer == "" {
		s.Issuer = "hmstudio"
	}
	if s.ExpiresTime == 0 {
		// 7天
		s.ExpiresTime = 604800
	}
	if s.SigningKey == "" {
		s.SigningKey = "hmstudio"
	}
	if s.RefreshExpiresTime == 0 {
		// 7天
		s.RefreshExpiresTime = 604800
	}
	if s.RefreshSigningKey == "" {
		s.RefreshSigningKey = "hmstudio"
	}
	// ctx.LogInfo("::JWTService#initJWT after " + s.Issuer + " " + s.SigningKey)
}

func (s *JWTService) CreateToken(req JWTClaims) (string, error) {
	claims := baseClaims{
		UUID:     req.UUID,
		ID:       req.ID,
		Username: req.Username,
		RoleId:   req.RoleId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Unix() + s.ExpiresTime, // 过期时间 7天  配置文件
			Issuer:    s.Issuer,                          // 签名的发行者
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(s.SigningKey))
}

func (s *JWTService) CreateTokenAndRefreshToken(req JWTClaims) (string, string, error) {
	claims := baseClaims{
		UUID:     req.UUID,
		ID:       req.ID,
		Username: req.Username,
		RoleId:   req.RoleId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Unix() + s.ExpiresTime, // 过期时间 7天  配置文件
			Issuer:    s.Issuer,                          // 签名的发行者
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenStr, err := token.SignedString([]byte(s.SigningKey))
	if err != nil {
		return "", "", err
	}

	claims = baseClaims{
		UUID:     req.UUID,
		ID:       req.ID,
		Username: req.Username,
		RoleId:   req.RoleId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Unix() + s.RefreshExpiresTime, // 过期时间 7天  配置文件
			Issuer:    s.Issuer,                                 // 签名的发行者
		},
	}
	refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	refreshTokenStr, err := refreshToken.SignedString([]byte(s.RefreshSigningKey))
	if err != nil {
		return "", "", err
	}

	return tokenStr, refreshTokenStr, err
}
func (s *JWTService) CreateTokenAndRefreshTokenV2(req JWTClaims) (*RefreshTokenResp, error) {
	nowTime := time.Now().Unix()
	tokenExpiresTime := nowTime + s.ExpiresTime
	claims := baseClaims{
		UUID:     req.UUID,
		ID:       req.ID,
		Username: req.Username,
		RoleId:   req.RoleId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: tokenExpiresTime, // 过期时间 7天  配置文件
			Issuer:    s.Issuer,         // 签名的发行者
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenStr, err := token.SignedString([]byte(s.SigningKey))
	if err != nil {
		return nil, err
	}

	claims = baseClaims{
		UUID:     req.UUID,
		ID:       req.ID,
		Username: req.Username,
		RoleId:   req.RoleId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: nowTime + s.RefreshExpiresTime, // 过期时间 7天  配置文件
			Issuer:    s.Issuer,                       // 签名的发行者
		},
	}
	refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	refreshTokenStr, err := refreshToken.SignedString([]byte(s.RefreshSigningKey))
	if err != nil {
		return nil, err
	}

	return &RefreshTokenResp{
		Token:        tokenStr,
		RefreshToken: refreshTokenStr,
		NowTime:      nowTime * 1000,
		ExpiresTime:  tokenExpiresTime * 1000,
	}, err
}

// 解析 token
func (s *JWTService) ParseToken(tokenString string) (*JWTClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &baseClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return []byte(s.SigningKey), nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}
	if token != nil {
		if claims, ok := token.Claims.(*baseClaims); ok && token.Valid {

			return &JWTClaims{
				UUID:     claims.UUID,
				ID:       claims.ID,
				Username: claims.Username,
				RoleId:   claims.RoleId,
			}, nil
		}
		return nil, TokenInvalid

	} else {
		return nil, TokenInvalid
	}
}

// 解析 token
func (s *JWTService) ParseRefreshToken(tokenString string) (*JWTClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &baseClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return []byte(s.RefreshSigningKey), nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}
	if token != nil {
		if claims, ok := token.Claims.(*baseClaims); ok && token.Valid {

			return &JWTClaims{
				UUID:     claims.UUID,
				ID:       claims.ID,
				Username: claims.Username,
				RoleId:   claims.RoleId,
			}, nil
		}
		return nil, TokenInvalid

	} else {
		return nil, TokenInvalid
	}
}
